package org.je.xml;

import java.util.List;

import org.je.ufo.Int;
import org.je.util.CharUtil;
import org.je.util.StringUtil;

/***
 * 对外使用的Api
 * 
 * @author fan yi feng
 */
class XmlGramUtil
{
	static int matchEndTag(Element parent, List<Element> list, int idx, String xml) {
		for (int i = idx; i < list.size(); ++i) {
			Element ele = list.get(i);

			if (ele.type == Element.XML_CLOSED_NODE || ele.type == Element.XML_NOTES || ele.type == Element.XML_CDATA || ele.type == Element.XML_TEXT) {
				parent.add(ele);
				continue;
			} else if (ele.type == Element.TAG_START) {
				int endTagIdx = matchEndTag(ele ,list ,i + 1 ,xml);
				parent.add(ele);
				i = endTagIdx;
				continue;
			} else if (ele.type == Element.TAG_END) {
				if (!ele.value.equals(parent.value)) {
					throw new ParseXmlException(error(xml ,"<" + parent.value + "></" + ele.value + ">not match." ,ele.offset));
				}
				parent.type = Element.XML_CLOSED_NODE;
				return i;
			} else {
				throw new ParseXmlException(error(xml ,"语法分析时发生错误,StartTag后面的字节点类型不正确[" + ele.type + "]" ,ele.offset));
			}
		}
		throw new ParseXmlException(error(xml ,"<" + parent.value + ">not match end tag." ,parent.offset));
	}

	static Element nextElement(String xml, Int offset) {
		if (offset.val() == xml.length()) {
			return null;
		}

		if (offset.val() + 3 >= xml.length()) {
			return getLastText(xml ,offset);
		}

		/************** 能到这一步，说明至少还有4个字符 ***************/

		char c1 = xml.charAt(offset.val());

		if (c1 == '<') // 如果遇到了 <
		{

			char c2 = xml.charAt(offset.val() + 1);

			if (c2 == '/') // 如果是 </
			{
				return getEndTag(xml ,offset);

			} else if (c2 == '!')// 如果是<!
			{
				char c3 = xml.charAt(offset.val() + 2);

				if (c3 == '[') { // 如果是 <![
					return getCDATA(xml ,offset);
				} else {
					return getNotes(xml ,offset);
				}

			} else {// 如果只是<开头
				return getTag(xml ,offset);
			}
		} else {
			return getText(xml ,offset);
		}

	}

	static Element getLastText(String xml, Int offset) {
		for (int i = offset.val(); i < xml.length(); ++i) {
			if (xml.charAt(i) == '<') {
				throw new ParseXmlException(error(xml ,"文本中不能包含<字符" ,i));
			}
		}

		Element ele = new Element();
		ele.type = Element.XML_TEXT;
		ele.offset = offset.val();
		// offset[0] = xml.length(); // 一定要更新offset的值
		offset.val(xml.length());
		ele.value = xml.substring(ele.offset);

		return ele;
	}

	static Element getTag(String xml, Int offset) {
		Element ele = new Element();

		ele.offset = offset.val();

		char c1 = xml.charAt(ele.offset + 1);

		if (!CharUtil.isNameStartChar(c1)) {
			throw new ParseXmlException(error(xml ,"tag开始字符不对[" + c1 + "]" ,ele.offset));
		}

		boolean findName = false;

		for (int i = ele.offset + 2; i < xml.length(); ++i) {
			char c = xml.charAt(i);

			if (!XmlAttrUtil.isNameChar(c)) {
				findName = true;
				ele.value = xml.substring(ele.offset + 1 ,i);

				if (CharUtil.isBlank(c)) // 空白
				{
					// offset[0] = i + 1; // i+1;
					offset.val(i + 1);
					break;

				} else if (c == '>') // >
				{
					// offset[0] = i + 1;
					offset.val(i + 1);
					ele.type = Element.TAG_START;
					return ele;
				} else if (c == '/') // /
				{
					if (i != xml.length() - 1 && xml.charAt(i + 1) == '>') { // />
						// offset[0] = i + 2;
						offset.val(i + 2);
						ele.type = Element.XML_CLOSED_NODE;
						return ele;
					} else { // 意外
						throw new ParseXmlException(error(xml ,"tag中出现不合法字符[" + c + "]" ,ele.offset));
					}
				} else {
					throw new ParseXmlException(error(xml ,"不合法的名字字符[" + c + "]" ,ele.offset));
				}
			}
		}

		if (!findName) {
			throw new ParseXmlException(error(xml ,"没有正常结束的tag" ,ele.offset));
		}

		trySaveAttributes(ele ,xml ,offset);

		return ele;

	}

	static Element getEndTag(String xml, Int offset) {
		// </a>
		Element ele = new Element();
		ele.type = Element.TAG_END;
		ele.offset = offset.val();

		char c1 = xml.charAt(ele.offset + 2);

		if (!CharUtil.isNameStartChar(c1)) {
			throw new ParseXmlException(error(xml ,"endTag开始字符不对[" + c1 + "]" ,ele.offset));
		}

		boolean findName = false;

		for (int i = ele.offset + 3; i < xml.length(); ++i) {
			char c = xml.charAt(i);

			if (!findName) {
				if (!XmlAttrUtil.isNameChar(c)) {
					ele.value = xml.substring(ele.offset + 2 ,i);

					if (CharUtil.isBlank(c)) {
						findName = true;
					} else if (c == '>') {
						// offset[0] = i + 1;
						offset.val(i + 1);
						return ele;
					} else {
						throw new ParseXmlException(error(xml ,"不合法的名字字符[" + c + "]" ,ele.offset));
					}
				}
			} else // findName
			{
				if (c == '>') {
					// offset[0] = i + 1;
					offset.val(i + 1);
					return ele;
				} else if (!CharUtil.isBlank(c)) {
					throw new ParseXmlException(error(xml ,"不合法的endTag结束符[" + c + "]" ,ele.offset));
				}
			}
		}

		throw new ParseXmlException(error(xml ,"没有正常结束的endTag" ,ele.offset));
	}

	private static Element getNotes(String xml, Int offset) {
		return getByPrefixAndSuffix(xml ,offset ,Element.XML_NOTES ,"<!--" ,"-->");
	}

	private static Element getCDATA(String xml, Int offset) {
		return getByPrefixAndSuffix(xml ,offset ,Element.XML_CDATA ,"<![CDATA[" ,"]]>");
	}

	private static Element getByPrefixAndSuffix(String xml, Int offset, byte type, String prefix, String suffix) {
		Element ele = new Element();
		ele.type = type;
		ele.offset = offset.val();

		if (!StringUtil.match(xml ,prefix ,offset.val())) {
			throw new ParseXmlException(error(xml ,"没有找到" + prefix + "前缀" ,offset.val()));
		}

		int maxEnd = xml.length() - suffix.length();

		for (int i = ele.offset + prefix.length(); i <= maxEnd; ++i)// 注意是<=
		{
			if (StringUtil.match(xml ,suffix ,i)) {
				// offset[0] = i + suffix.length();
				offset.val(i + suffix.length());
				ele.value = xml.substring(ele.offset + prefix.length() ,i);
				return ele;
			}
		}

		throw new ParseXmlException(error(xml ,"没有找到" + suffix ,ele.offset));
	}

	private static Element getText(String xml, Int offset) {
		Element ele = new Element();
		ele.type = Element.XML_TEXT;
		ele.offset = offset.val();

		for (int i = ele.offset + 1; i < xml.length(); ++i) {
			if (xml.charAt(i) == '<') {
				// offset[0] = i;
				offset.val(i);
				ele.value = xml.substring(ele.offset ,i);
				return ele;
			}
		}

		// offset[0] = xml.length();
		offset.val(xml.length());
		ele.value = xml.substring(ele.offset);

		return ele;
	}

	static String error(String xml, String msg, int idx) {
		StringBuilder bf = new StringBuilder(xml.length() + 100);
		bf.append("xml-analyze-error:" + msg + "\r\n");
		bf.append(xml.substring(0 ,idx));
		bf.append("\r\n^^^^^^^^^^^^\r\n");
		bf.append(xml.substring(idx));
		return bf.toString();
	}

	static int nextNotBlank(String source, int offset) {

		for (int i = offset; i < source.length(); ++i) {
			char c = source.charAt(i);

			if (!CharUtil.isBlank(c)) {
				return i;
			}
		}

		return -1;
	}

	/************************* 下面是分析TAG的Attributes ************************/
	private static void trySaveAttributes(Element ele, String xml, Int offset) {
		while( true ) {
			String name = XmlAttrUtil.getAttName(xml ,offset ,false);

			if (name.equals(">")) {
				ele.type = Element.TAG_START;
				break;
			} else if (name.equals("/>")) {
				ele.type = Element.XML_CLOSED_NODE;
				break;
			} else {
				XmlAttrUtil.findEqSymbol(xml ,offset);
				String value = XmlAttrUtil.getAttValue(xml ,offset ,false);
				ele.addAtt(name ,value);
			}
		}
	}

}
